package net.wangds.persistence.def.comp;

import net.wangds.data.ColumnOrientedDataTypeEnum;
import net.wangds.data.DataTypeEnum;
import net.wangds.data.TableCateEnum;
import net.wangds.data.TableTypeEnum;
import net.wangds.log.helper.LogHelper;
import net.wangds.persistence.def.*;
import net.wangds.data.ann.ColumnDef;
import net.wangds.persistence.def.entity.DictFieldEntity;
import net.wangds.persistence.def.entity.DictTableEntity;
import net.wangds.persistence.def.repo.db.DictFieldRepo;
import net.wangds.persistence.def.repo.db.DictTableRepo;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import javax.persistence.Table;
import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.List;

/**
 * .
 * <p></p>
 *
 * @author 王东石 2020/11/9 0:28.
 */
@Component
public abstract class AbstractTableDefComp implements TableDef, InitializingBean {

    @Autowired
    private TableDefManager mmCotMngr;


    /**
     * 获得列存储对象的Class定义.
     * <p> 需要用{@linkplain javax.persistence.Entity}、{@linkplain Table}修饰定义实体类，并且
     * 用{@linkplain net.wangds.data.ann.TableDef}修饰定义列存储对应的实体类.<br/>
     * 例如:<br/><pre>@ColOriTable(displayName = "运营商实体", beanClass = "net.wangds.tcec.tcec102d2.info.OperatorInfo")</pre>
     * <pre>@Entity</pre>
     * <pre>@Table(name="tcec102_col_operators")</pre>
     * </p>
     * @return 类定义.
     */
    protected abstract Class<?> getFieldClass();

    @Override
    public void onInstall(DictTableRepo repDicTab, DictFieldRepo repDicFld) {

        DictTableEntity tblDef = updateColTblDef(repDicTab);
        updateFieldDef(tblDef, repDicFld);

    }

    private List<DictFieldEntity> updateFieldDef(DictTableEntity tblDef, DictFieldRepo repDicFld) {
        List<DictFieldEntity> res = new ArrayList<>();


        try {
            Class<?> clzBean = Class.forName(tblDef.getBeanClass());
            Class<?> clzFld = getFieldClass();

            procClassField(clzBean, clzFld, tblDef, repDicFld);


        } catch (ClassNotFoundException e) {
            LogHelper.error(e);
        }

        return res;
    }

    /**
     * 递归处理实体类总的字段定义.
     * @param clzBean 实体类.
     * @param clzFld 字段类.
     *               <p>列式存储时需要特殊关注.</p>
     * @param tblDef 表定义.
     * @param repDicFld 字段字典表操作对象.
     */
    private void procClassField(Class<?> clzBean, Class<?> clzFld, DictTableEntity tblDef, DictFieldRepo repDicFld){


        for(Field fld :clzBean.getFields()){
            //String fldName = fld.getName();
            //LogHelper.dev("字段名:"+fldName);
            procField(fld,  tblDef, repDicFld);
        }


        for(Field fld :clzBean.getDeclaredFields()){
            procField(fld, tblDef, repDicFld);
            //String fldName = fld.getName();
            //LogHelper.dev("字段名="+fldName);
        }

        Class<?> parentClz = clzBean.getSuperclass();
        if(parentClz!=null&&parentClz!=Object.class){
            procClassField(parentClz, clzFld, tblDef, repDicFld);
        }

    }

    protected void procField(Field fld, DictTableEntity tblDef, DictFieldRepo repDicFld) {

        ColumnDef annFld = fld.getAnnotation(ColumnDef.class);

        if(annFld==null){
            return;
        }

        String schemaname = tblDef.getSchemaname();
        String tablename = tblDef.getTablename();
        String fieldname = fld.getName();
        String displayname = annFld.displayname();
        DataTypeEnum dataType = annFld.dataType();
        int ver = annFld.version();

        ColumnOrientedDataTypeEnum colDataType = annFld.colDataType();
        String memo = annFld.memo();

        DictFieldEntity fldDef = repDicFld.findBySchemanameAndTablenameAndFieldname(
                schemaname, tablename, fieldname
        ).orElseGet(() -> DictFieldEntity.builder().build());


        fldDef.setSchemaname(schemaname);
        fldDef.setTablename(tablename);
        fldDef.setFieldname(fieldname);
        fldDef.setDisplayname(displayname);
        fldDef.setDataType(dataType);
        fldDef.setColumnOrientedDataType(colDataType);
        fldDef.setMemo(memo);
        fldDef.setVersion(ver);

        repDicFld.saveAndFlush(fldDef);

    }


    protected DictTableEntity updateColTblDef(DictTableRepo repDicTab){

        Class<?> clzField = getFieldClass();
        Table annTbl = clzField.getAnnotation(Table.class);
        net.wangds.data.ann.TableDef annColOriTbl = clzField.getAnnotation(net.wangds.data.ann.TableDef.class);
        if (annTbl != null&&annColOriTbl!=null) {
            String schema = annTbl.schema();
            String tablename = annTbl.name();
            String displayName = annColOriTbl.displayName();
            String beanClass = annColOriTbl.beanClass();
            String memo = annColOriTbl.memo();
            TableTypeEnum type = annColOriTbl.tableType();
            TableCateEnum cate = annColOriTbl.category();
            String fieldClass = clzField.getName();
            int ver = annColOriTbl.version();

            DictTableEntity entity = repDicTab.findBySchemanameAndTablename(schema, tablename)
                    .orElseGet(() -> DictTableEntity.builder().build());

            entity.setSchemaname(schema);
            entity.setTablename(tablename);
            entity.setDisplayname(displayName);
            entity.setBeanClass(beanClass);
            entity.setFieldClass(fieldClass);
            entity.setCate(cate);
            entity.setType(type);
            entity.setMemo(memo);
            entity.setVersion(ver);

            repDicTab.saveAndFlush(entity);

            return entity;

        }

        return null;
    }

    @Override
    public void afterPropertiesSet() {
        mmCotMngr.install(this);
    }
}
